package edu.bw.utils;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.cglib.proxy.Enhancer;
import org.springframework.cglib.proxy.MethodInterceptor;
import org.springframework.context.ApplicationContext;
import org.springframework.stereotype.Component;

import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;

@Component
public class TransactionProxyFactory {

    @Autowired
    private TransactionManager transactionManager;

    @Autowired
    private ApplicationContext applicationContext;

    public  Object creatProxyInstance(Class cls){
        if (cls.getInterfaces().length!=0){//判断传递过来的被代理类是否实现了接口
            return Proxy.newProxyInstance(
                    cls.getClassLoader(),
                    cls.getInterfaces(),
                    ((proxy, method, args) -> invokeWithTransaction(cls,method,args))
            );

        }else {
            //当前被代理的类没有实现任何接口，此时使用CGLIB实现动态代理，将被代理类作为父类
            //1.利用Enhancer增强被代理类,通过它生成代理类对象
            Enhancer enhancer = new Enhancer();

            //2.设置将来生成的代理类的父类
            enhancer.setSuperclass(cls);


            //设置回调函数，当通过代理类对象调用方法时候，转调回调函数
            enhancer.setCallback(
                    (MethodInterceptor)(obj, method, args, proxy)->invokeWithTransaction(cls,method,args)
            );
            return enhancer.create();


        }


    }

    private Object invokeWithTransaction(Class cls, Method method, Object[] args) throws IllegalAccessException, InvocationTargetException {
        Object retVal=null;
        if (method.getName().startsWith("find")){
            retVal= method.invoke(applicationContext.getBean(cls), args);
        }else {
            try {
                transactionManager.beginTransaction();
                retVal= method.invoke(applicationContext.getBean(cls), args);
                transactionManager.commit();
            } catch (Exception e) {
                transactionManager.rollback();
                e.printStackTrace();
            } finally {
                transactionManager.release();
            }
        }
        return retVal;
    }
}
